package org.auth.service.config;

import javax.annotation.Resource;
import javax.sql.DataSource;

import org.auth.service.service.UserDetailServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

/**
 * <p>
 * <code>AuthorizationServerConfig</code>
 * </p>
 * Description: 授权/认证服务器配置
 * 
 * @author javadaily
 * @date 2020/2/26 16:26
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
	 @Autowired
	    private AuthenticationManager authenticationManager;

	    @Autowired
	    private DataSource dataSource;

	    @Autowired
	    private RedisConnectionFactory redisConnectionFactory;

	    @Autowired
	    private UserDetailServiceImpl userDetailService;
	    
	    @Resource
	    RedisTemplate<String,String> redis;

	    @Bean
	    public TokenStore tokenStore() {
	        return new RedisTokenStore(redisConnectionFactory);
	    }

	    @Override
	    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
	        security
	                .allowFormAuthenticationForClients()
	                .tokenKeyAccess("permitAll()")
	                .checkTokenAccess("isAuthenticated()");
	    }

	    /**
	     * 配置 oauth_client_details【client_id和client_secret等】信息的认证【检查ClientDetails的合法性】服务
	     * 设置 认证信息的来源：数据库 (可选项：数据库和内存,使用内存一般用来作测试)
	     * 自动注入：ClientDetailsService的实现类 JdbcClientDetailsService (检查 ClientDetails 对象)
	     * 这个方法主要是用于校验注册的第三方客户端的信息，可以存储在数据库中，默认方式是存储在内存中，如下所示，注释掉的代码即为内存中存储的方式
	     */
	    @Override
	    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
	       // clients.withClientDetails(clientDetails());
	        //配置在内存中，也可以从数据库中获取
	    	String a=new BCryptPasswordEncoder().encode("11");
	        clients.inMemory() // 使用in-memory存储
	                .withClient("123") // client_id   android
	                .scopes("read")
	                .secret(a)  // client_secret   android
	                .authorizedGrantTypes("password", "authorization_code", "refresh_token") // 该client允许的授权类型
	                .and()
	                .withClient("webapp") // client_id
	                .scopes("read")
	                //.secret("webapp")  // client_secret
	                .authorizedGrantTypes("implicit")// 该client允许的授权类型
	                .and()
	                .withClient("browser")
	                .authorizedGrantTypes("refresh_token", "password")
	                .scopes("read");
	    }
	    @Bean
	    public ClientDetailsService clientDetails() {
	        return new JdbcClientDetailsService(dataSource);
	    }


	    @Override
	    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
	        endpoints.tokenStore(tokenStore())
	                .userDetailsService(userDetailService)
	                .authenticationManager(authenticationManager);
	        endpoints.tokenServices(defaultTokenServices());
	     
	    }

	    /**
	     * <p>注意，自定义TokenServices的时候，需要设置@Primary，否则报错，</p>
	     * 自定义的token
	     * 认证的token是存到redis里的
	     * @return
	     */
	    @Primary
	    @Bean
	    public DefaultTokenServices defaultTokenServices(){
	        DefaultTokenServices tokenServices = new DefaultTokenServices();
	        
	          tokenServices.setTokenStore(tokenStore());
	        tokenServices.setSupportRefreshToken(true);
	         // token有效期自定义设置，默认12小时
	        tokenServices.setAccessTokenValiditySeconds(60 * 60);
	        // refresh_token默认30天
	        tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 7);
	        return tokenServices;
	    }
	
}